﻿using DataProcess.Authable.Basic;
using DataProcess.Encryption;
using DataProcess.Error;
using System;
using System.Collections.Generic;
using static DataProcess.Encryption.HMACMaker;

namespace DataProcess.Authable
{
    namespace Basic
    {
        /// <summary>
        /// CRC工具
        /// </summary>
        public class CRC
        {

            /// <summary>
            /// 通过二进制字符串验证
            /// </summary>
            /// <param name="code">当前码</param>
            /// <param name="gencode">生成方程</param>
            /// <returns></returns>
            public static bool VerifyCRC(string code, string gencode)
            {
                string r = getLeft(code, gencode);
                return ToInt(r) == 0;
            }
            /// <summary>
            /// 通过正整数值进行验证
            /// </summary>
            /// <param name="code">当前码</param>
            /// <param name="gencode">生成方程</param>
            /// <returns></returns>
            public static bool VerifyCRC(int code, int gencode)
            {
                string c = ToBin(code);
                string chushu = ToBin(gencode);
                string r = getLeft(c, chushu);
                return ToInt(r) == 0;
            }
            /// <summary>
            /// 添加CRC校验码
            /// </summary>
            /// <param name="number">数据</param>
            /// <param name="gencode">生成方程</param>
            /// <returns></returns>
            public static string AppendCRC(int number, int gencode)
            {
                string orgin = ToBin(number);
                string chushu = ToBin(gencode);
                return AppendCRC(orgin, chushu);
            }
            /// <summary>
            /// 通过二进制字符串添加CRC校验码
            /// </summary>
            /// <param name="number">数据</param>
            /// <param name="gencode">生成方程</param>
            /// <returns></returns>
            public static string AppendCRC(string number, string gencode)
            {
                string orgin = number;
                string chushu = gencode;
                string text = PadOrgin(orgin, chushu);
                string left = getLeft(text, chushu);
                return orgin + left;
            }

            static string getLeft(string orgin, string chushu, int index = 0, string beichushu = "")
            {
                if (index == 0)
                {
                    beichushu = orgin.Substring(0, chushu.Length);
                    index += chushu.Length;
                }
                else
                {
                    if (beichushu.Length < chushu.Length)
                    {
                        int n = chushu.Length - beichushu.Length;
                        if (index + n > orgin.Length)
                        {
                            return (beichushu + orgin.Substring(index)).PadLeft(chushu.Length - 1, '0');
                        }
                        beichushu += orgin.Substring(index, n);
                        index += n;
                    }
                    else if (beichushu.Length > chushu.Length)
                        throw new ArgumentException("计算有误");
                }
                string left = cal(beichushu, chushu);
                if (index == orgin.Length - 1)
                    return left;
                else
                    return getLeft(orgin, chushu, index, left);

            }
            static string ToBin(int num)
            {
                return Convert.ToString(num, 2);
            }
            static int ToInt(string n)
            {
                return Convert.ToInt32(n, 2);
            }
            static string cal(string beichu, string chu)
            {
                int a = ToInt(beichu);
                int b = ToInt(chu);
                return ToBin(a ^ b);
            }
            static string PadOrgin(string orgin, string g)
            {
                int chushumax = g.Length - 1;
                return orgin.PadRight(chushumax + orgin.Length, '0');
            }
        }

        /// <summary>
        /// 随机码生成器
        /// </summary>
        public class CodeGenerater : ICodeGenerater<int>
        {

            protected Random random = new Random();
            /// <summary>
            /// 当生成单个码时的事件
            /// </summary>
            /// <param name="index">码所在位置</param>
            /// <returns></returns>
            public delegate int? OnGenCode(int index);
            /// <summary>
            /// 判定当前码是否为数字的事件
            /// </summary>
            /// <param name="index"></param>
            /// <returns></returns>
            public delegate bool? OnIsNum(int index);
            /// <summary>
            /// 当生成随机码时
            /// </summary>
            public event OnGenCode OnGenString;
            /// <summary>
            /// 当提供是否为数字时
            /// </summary>
            public event OnIsNum OnIsNumber;
            /// <summary>
            /// 当前单个码是否为数字
            /// </summary>
            /// <returns></returns>
            protected virtual bool isNum()
            {
                return random.Next(0, 3) > 1;
            }
            /// <summary>
            /// 获取单个码
            /// </summary>
            /// <param name="isNum"></param>
            /// <returns></returns>
            protected virtual int getSingle(bool isNum)
            {
                if (isNum)
                {
                    return (random.Next(10) + 48);
                }
                else
                {
                    return (random.Next(26) + 65);
                }
            }
            /// <summary>
            /// 生成随机码
            /// </summary>
            /// <param name="length"></param>
            /// <returns></returns>
            string GenerateCode(int length)
            {
                if (length <= 0)
                {
                    throw new GeneraterException("随机码长度不能≤0");
                }
                string res = "";
                for (int i = 0; i < length; i++)
                {
                    bool isnum = isNum();
                    if (OnIsNumber != null)
                    {
                        bool? tmp = OnIsNumber(i);
                        if (tmp != null)
                        {
                            isnum = (bool)tmp;
                        }
                    }
                    int nowchar = getSingle(isnum);
                    if (OnGenString != null)
                    {
                        int? t = OnGenString(i);
                        if (t != null)
                        {
                            nowchar = (int)t;
                        }
                        else if (t < 0)
                        {
                            throw new GeneraterException("得到的编码错误，不存在小于0的通用码");
                        }
                    }
                    res += (char)nowchar;
                }
                return res;
            }

            public string Generate(int length)
            {
                return GenerateCode(length);
            }
        }

        /// <summary>
        /// 代码生成接口
        /// </summary>
        /// <typeparam name="Targ"></typeparam>
        public interface ICodeGenerater<Targ>
        {
            /// <summary>
            /// 最终产生代码
            /// </summary>
            /// <param name="arg"></param>
            /// <returns></returns>
            string Generate(Targ arg);
        }

        /// <summary>
        /// 验证器基类
        /// </summary>
        public abstract class Authenticator<TKey, TContent>
        {

            /// <summary>
            /// 当前代码
            /// </summary>
            public virtual string Code
            {
                get;
                protected set;
            }
            /// <summary>
            /// 密钥
            /// </summary>
            public virtual TKey Key
            {
                get;
                set;
            }
            public delegate void DataComputed(byte[] keydata);
            public delegate string CodeComputed(byte[] rawdata, string nowcode);
            /// <summary>
            /// 当密钥处理完成时
            /// </summary>
            public event DataComputed OnKeyComputed;
            /// <summary>
            /// 当原始数据产生时
            /// </summary>
            public event DataComputed OnRawDataComputed;
            /// <summary>
            /// 当输入内容处理完成时
            /// </summary>
            public event DataComputed OnContentComputed;

            /// <summary>
            /// 当生成代码时
            /// </summary>
            public event CodeComputed OnResult;

            /// <summary>
            /// 将密钥转化为基础数据
            /// </summary>
            /// <param name="key"></param>
            /// <returns></returns>
            protected abstract byte[] KeyToCode(TKey key);
            /// <summary>
            /// 将内容转化为基础数据
            /// </summary>
            /// <param name="content"></param>
            /// <returns></returns>
            protected abstract byte[] ContentToCode(TContent content);
            /// <summary>
            /// 根据密钥和内容产生基础数据
            /// </summary>
            /// <param name="key"></param>
            /// <param name="content"></param>
            /// <returns></returns>
            protected abstract byte[] ToRawCode(byte[] key, byte[] content);
            /// <summary>
            /// 根据产生的基础数据生成字符码
            /// </summary>
            /// <param name="raw"></param>
            /// <returns></returns>
            protected abstract string ToUseableCode(byte[] raw);

            /// <summary>
            /// 产生代码
            /// </summary>
            /// <param name="key"></param>
            /// <param name="content"></param>
            /// <returns></returns>
            protected string GenerateCode(TKey key, TContent content)
            {
                byte[] keyarr = KeyToCode(key);
                if (OnKeyComputed != null)
                    OnKeyComputed(keyarr);
                byte[] contentarr = ContentToCode(content);
                if (OnContentComputed != null)
                    OnContentComputed(contentarr);
                byte[] raw = ToRawCode(keyarr, contentarr);
                if (OnRawDataComputed != null)
                    OnRawDataComputed(raw);
                string res = ToUseableCode(raw);
                if (OnResult != null)
                {
                    res = OnResult(raw, res);
                }
                Code = res;
                return res;
            }
            /// <summary>
            /// 验证代码是否合法
            /// </summary>
            /// <param name="keyCode">待验证的代码</param>
            /// <param name="key">密钥</param>
            /// <returns></returns>
            public virtual bool VerifyCode(string keyCode, TKey key)
            {
                return Code.Equals(keyCode);
            }
            public bool VerifyCode(string keyCode)
            {
                return VerifyCode(keyCode, Key);
            }

        }
    }
    /// <summary>
    /// 一次性密码验证器，基于HMAC
    /// </summary>
    public class OneTimeAuthenticator : Basic.Authenticator<string, long>
    {

        public OneTimeAuthenticator()
        {
            Init();
        }
        public OneTimeAuthenticator(string key)
        {
            Init();
            this.Key = key;
        }
        protected virtual void Init()
        {
            Key = "";
            this.CodingLevel = Level.SHA1;
        }

        long HashToInt(byte[] arr, int offset, int l = 4)
        {
            string temp = "";
            for (int i = offset; i < offset + l; i++)
            {
                temp += arr[i].ToString("X2");
            }
            return Convert.ToInt64(temp, 16);
        }
        /// <summary>
        /// 加密级别
        /// </summary>
        public HMACMaker.Level CodingLevel
        {
            get;
            set;
        }
        /// <summary>
        /// 位数
        /// </summary>
        public int Digit
        {
            get => digit;
            set
            {
                if (value > 0 && value <= 10)
                {
                    digit = value;
                }
                else
                    throw new ArgumentOutOfRangeException("数位有效值：1-10", "位数超出有效范围");
            }
        }
        private int digit = 6;
        protected override byte[] ContentToCode(long content)
        {
            byte[] arr = BitConverter.GetBytes(content);
            Array.Reverse(arr);
            return arr;
        }

        protected override byte[] KeyToCode(string key)
        {
            return Base32.Decode(key);
        }

        protected override byte[] ToRawCode(byte[] key, byte[] content)
        {
            return HMAC(key, content, CodingLevel);
        }
        long[] CodeDigit = { 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000 };
        protected override string ToUseableCode(byte[] hash)
        {

            int offset = hash[hash.Length - 1] & 0xF;
            // Grab a positive integer value starting at the given offset.
            long truncatedHash = HashToInt(hash, offset, 4) & 0x7FFFFFFF;
            long pinValue = truncatedHash % CodeDigit[Digit - 1];
            return pinValue.ToString().PadLeft(Digit, '0');
        }
    }
    /// <summary>
    /// HOTP验证器
    /// </summary>
    public class HMACbasedOneTimeAuthenticator : OneTimeAuthenticator, ICodeGenerater<long>
    {
        /// <summary>
        /// 当前计数
        /// </summary>
        public long Count
        {
            get;
            private set;
        }
        protected override void Init()
        {
            Count = 0;
            base.Init();
        }
        /// <summary>
        /// 递增进行计数
        /// </summary>
        /// <returns></returns>
        public string Generate()
        {
            return GenerateCode(Key, ++Count);
        }
        public string Generate(long arg)
        {
            Count = arg;
            return GenerateCode(Key, arg);
        }
    }

    /// <summary>
    /// 基于时间的一次性密钥验证器
    /// </summary>
    public class TimeBasedOneTimeAuthenticator : OneTimeAuthenticator, ICodeGenerater<DateTime>
    {

        public override string Code { get => Generate(DateTime.Now); protected set => base.Code = value; }


        /// <summary>
        /// 周期时间
        /// </summary>
        public TimeSpan Span
        {
            get;
            set;
        }
        /// <summary>
        /// 总起始时间
        /// </summary>
        public DateTime StartTime
        {
            get;
            set;
        }
        /// <summary>
        /// 当前周期起始时间
        /// </summary>
        public DateTime NowCodeTime
        {
            get
            {
                DateTime now = DateTime.Now;
                long s = (long)(now.ToUniversalTime() - StartTime).TotalSeconds;
                int t = (int)(s / Span.TotalSeconds);
                DateTime d = StartTime.Add(TimeSpan.FromSeconds(t * Span.TotalSeconds));
                if (d >= now)
                    return d.Subtract(Span);
                else
                    return d;
            }
        }
        /// <summary>
        /// 当前周期剩余时间
        /// </summary>
        public TimeSpan Remaining
        {
            get
            {
                return Span - (DateTime.Now.ToUniversalTime() - NowCodeTime);
            }
        }

        long getStep()
        {
            return (long)Span.TotalSeconds;
        }
        long UnixTime(DateTime time)
        {
            TimeSpan span = time.ToUniversalTime() - StartTime;
            long timeSinceStartTime = (long)span.TotalSeconds;
            long step = getStep();
            if (timeSinceStartTime >= 0)
            {
                return timeSinceStartTime / step;
            }
            else
            {
                return (timeSinceStartTime - (step - 1)) / step;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="key">密钥</param>
        /// <param name="step">间隔（秒）</param>
        public TimeBasedOneTimeAuthenticator(string key, long step = 30)
        {
            this.Key = key;
            Span = TimeSpan.FromSeconds(step);

        }
        protected override void Init()
        {
            Span = TimeSpan.FromSeconds(30);
            StartTime = new DateTime(1970, 1, 1);
            base.Init();
        }
        public override bool VerifyCode(string keyCode, string key)
        {
            return keyCode.ToLower().Equals(Generate(DateTime.Now).ToLower());
        }

        public string Generate(DateTime arg)
        {
            return this.GenerateCode(Key, UnixTime(arg));
        }
    }


    /// <summary>
    /// 含带特征码的随机码
    /// </summary>
    public class SpecialCodeGenerater : CodeGenerater
    {
        private int nowlength;
        /// <summary>
        /// 当前随机码长度
        /// </summary>
        public int NowLength
        {
            get => nowlength;
            set
            {
                InitSeed(Special, value);
                nowlength = value;
            }
        }
        /// <summary>
        /// 默认填充字符
        /// </summary>
        public char FillChar
        {
            get;
            set;
        }

        private string spec;
        /// <summary>
        /// 当前特征码
        /// </summary>
        public string Special
        {
            get => spec;
            set
            {
                InitSeed(value, NowLength);
                spec = value;
            }
        }

        private Dictionary<string, Random> Now = new Dictionary<string, Random>();
        public SpecialCodeGenerater(string special, int length = 4)
        {
            spec = special;
            FillChar = ' ';
            nowlength = length;
            InitSeed(Special, NowLength);
        }

        private char[] Fill(string Text, int lengh)
        {

            if (Text.Length < lengh)
            {
                int num = lengh - Text.Length;
                for (int i = 0; i < num; i++)
                {
                    Text += FillChar;
                }
            }
            else if (Text.Length > lengh)
            {
                throw new GeneraterException("特征码长度超出原长度,无法进行填充");
            }
            return Text.ToCharArray();
        }

        private int getFillLength(int genlength, int orginlength)
        {
            int t = orginlength;
            while (t % genlength != 0)
            {
                t++;
            }
            return t;
        }

        private string[] Split(string orgin, int totalLengh)
        {
            int orginneed = getFillLength(totalLengh, orgin.Length);
            if (totalLengh > orgin.Length)
            {
                orgin = new string(Fill(orgin, totalLengh));
            }
            else
            {
                if (orginneed != totalLengh)
                {
                    orgin = new string(Fill(orgin, orginneed));
                }
            }
            double ys = orgin.Length / (double)totalLengh;
            int singlenum = orgin.Length / totalLengh;
            if (ys > singlenum)
            {
                singlenum++;
            }

            List<string> list = new List<string>();
            char[] p = orgin.ToCharArray();
            string tmp = "";
            for (int i = 0; i < orgin.Length; i++)
            {
                if (i % singlenum == 0 && i >= singlenum)
                {
                    list.Add(tmp);
                    tmp = "";
                }
                tmp += p[i];
            }
            list.Add(new string(Fill(tmp, singlenum)));
            return list.ToArray();
        }

        private int GetSpecialNumber(string s)
        {
            int total = 0;
            foreach (char t in s.ToCharArray())
            {
                total += t;
            }
            return total;
        }
        protected void InitSeed(string special, int length)
        {
            Now.Clear();
            Now = GetRandoms(special, length);
        }
        public bool Verify(string special, string Code)
        {
            Dictionary<string, Random> seed = GetRandoms(special, Code.Length);
            for (int i = 0; i < 1280000; i++)
            {
                if (Code.Equals(gen(seed)))
                {
                    return true;
                }
            }
            return false;
        }

        private string gen(Dictionary<string, Random> genseed)
        {
            string res = "";
            foreach (Random r in genseed.Values)
            {
                random = r;
                res += base.Generate(1);
            }
            return res;
        }
        /// <summary>
        /// 产生指定长度的特征码
        /// </summary>
        /// <param name="length"></param>
        /// <returns></returns>
        public new string Generate(int length)
        {
            return GenerateCode(length, Special);
        }

        private void Check(int length, string special)
        {
            bool needInit = false;
            if (length != nowlength)
            {
                nowlength = length;
                needInit = true;
            }
            if (!special.Equals(Special))
            {
                Special = special;
                needInit = true;
            }
            if (needInit)
            {
                InitSeed(special, length);
            }
        }
        /// <summary>
        /// 按默认参数产生特征码
        /// </summary>
        /// <returns></returns>
        public string GenerateCode()
        {
            if (Special.Length > 0 && NowLength > 0)
            {
                return GenerateCode(NowLength, Special);
            }
            else
            {
                throw new GeneraterException("特征码为空");
            }
        }
        /// <summary>
        /// 按指定参数产生特征码
        /// </summary>
        /// <param name="length">长度</param>
        /// <param name="special">特征信息</param>
        /// <returns></returns>
        public string GenerateCode(int length, string special)
        {
            if (special.Length == 0)
            {
                throw new GeneraterException("特征码为空");
            }
            Check(length, special);
            return gen(Now);
        }
        private Dictionary<string, Random> GetRandoms(string special, int l)
        {
            string[] p = Split(special, l);
            Dictionary<string, int> keys = new Dictionary<string, int>();
            Dictionary<string, Random> AllRandoms = new Dictionary<string, Random>();
            foreach (string t in p)
            {
                if (!keys.ContainsKey(t))
                {
                    AllRandoms.Add(t, new Random(GetSpecialNumber(t)));
                    keys.Add(t, 0);
                }
                else
                {
                    keys[t]++;
                    AllRandoms.Add(t + "-" + keys[t], new Random(GetSpecialNumber(t + "-" + keys[t])));
                }
            }
            return AllRandoms;
        }
    }

}
